package dao

import (
	"bytes"
	models "codeless/model"
	"github.com/huandu/go-sqlbuilder"
	"reflect"
	"strings"
)

func QueryRawSql(sql string, arg map[string]interface{}, ptr interface{}) {
	sb := sqlbuilder.BuildNamed(sql, arg)
	buildSql, sqlArgs := sb.Build()
	println(buildSql)
	session.Raw(buildSql, sqlArgs...).Find(ptr)
}
func ExecRawSql(sql string, arg map[string]interface{}) (int64, error) {
	sb := sqlbuilder.BuildNamed(sql, arg)
	buildSql, sqlArgs := sb.Build()
	println(buildSql)
	tx := session.Exec(buildSql, sqlArgs...)
	return tx.RowsAffected, tx.Error
}
func Struct2Map(obj interface{}) map[string]interface{} {
	t := reflect.TypeOf(obj)
	v := reflect.ValueOf(obj)

	var data = make(map[string]interface{})
	for i := 0; i < t.NumField(); i++ {
		tagName := t.Field(i).Tag.Get("json")
		if tagName != "" && tagName != "-" {
			data[tagName] = v.Field(i).Interface()
		}
	}
	return data
}
func PageSql(query models.CrudPage, pageIndex, pageSize int) (models.PageModelLay, error) {

	sqlData, sqlErr := PageDrySql(query)
	if sqlErr != nil {
		return models.PageModelLay{}, sqlErr
	}
	var itemsCount int64
	argMap := sqlData.Args
	var tableJsonData = models.PageModelLay{}
	if query.Count != "false" {
		QueryRawSql(sqlData.CountSql, argMap, &itemsCount)
		tableJsonData.BuildPageInfo(pageIndex, pageSize, int(itemsCount))
		list := make([]map[string]interface{}, 0)
		argMap["pageIndex"] = tableJsonData.PageIndex
		argMap["pageSize"] = tableJsonData.PageSize
		QueryRawSql(sqlData.PageSql, argMap, &list)
		tableJsonData.Rows = list
	} else {
		list := make([]map[string]interface{}, 0)
		QueryRawSql(sqlData.PageSql, argMap, &list)
		tableJsonData.BuildPageInfo(1, 20, len(list))
		tableJsonData.Rows = list
	}
	return tableJsonData, nil
}
func PageDrySql(query models.CrudPage) (models.SqlPageExplain, error) {

	sb := &bytes.Buffer{}
	countBuf := &bytes.Buffer{}
	sb.WriteString("SELECT ")
	sb.WriteString(query.Select)
	sb.WriteString(" FROM ")
	sb.WriteString(query.From)
	if query.Count != "false" {
		countBuf.WriteString("SELECT count(*) FROM ")
		if query.Count != "" {
			countBuf.WriteString(query.Count)
		} else {
			countBuf.WriteString(query.From)
		}
	}
	preSql := sb.String()
	countSql := countBuf.String()
	if len(query.Where) >= 1 {
		whereMap, whereErr := query.WhereSqlMap(preSql)
		if whereErr != nil {
			return models.SqlPageExplain{}, whereErr
		}
		for k, v := range whereMap {
			whereName := "${" + k + "}"
			if strings.Contains(preSql, whereName) {
				preSql = strings.ReplaceAll(preSql, whereName, v)
				if query.Count != "false" {
					countSql = strings.ReplaceAll(countSql, whereName, v)
				}
			} else {
				preSql = preSql + v
				if query.Count != "false" {
					countSql = countSql + v
				}
			}
		}
	}
	//count sql 里面去掉order by
	countSqlPart := ""
	if query.Count != "false" {
		countSp := strings.Split(countSql, "order by")
		countSqlPart = countSp[0]
		preSql = preSql + " limit ${pageIndex},${pageSize}"
	}
	return models.SqlPageExplain{
		CountSql: countSqlPart,
		PageSql:  preSql,
		Args:     query.GetArgValMap(),
	}, nil
}
func ListSql(query models.CrudQuery) ([]map[string]interface{}, error) {
	sqlData, sqlErr := ListDrySql(query)
	if sqlErr != nil {
		return nil, sqlErr
	}
	info := make([]map[string]interface{}, 0)
	QueryRawSql(sqlData.Sql, sqlData.Args, &info)
	return info, nil
}
func ListDrySql(query models.CrudQuery) (models.SqlExplain, error) {
	if query.Sql != "" {
		return models.SqlExplain{
			Sql:  query.Sql,
			Args: query.GetArgValMap(),
		}, nil
	}
	sb := &bytes.Buffer{}
	sb.WriteString("SELECT ")
	sb.WriteString(query.Select)
	sb.WriteString(" FROM ")
	sb.WriteString(query.From)
	preSql := sb.String()
	if len(query.Where) >= 1 {
		whereMap, sqlErr := query.WhereSqlMap(preSql)
		if sqlErr != nil {
			return models.SqlExplain{}, sqlErr
		}
		for k, v := range whereMap {
			whereName := "${" + k + "}"
			if strings.Contains(preSql, whereName) {
				preSql = strings.ReplaceAll(preSql, whereName, v)
			} else {
				preSql = preSql + v
			}
		}
	}
	return models.SqlExplain{
		Sql:  preSql,
		Args: query.GetArgValMap(),
	}, nil
}
func GetSql(query models.CrudQuery) (map[string]interface{}, error) {
	sqlData, sqlErr := GetDrySql(query)
	if sqlErr != nil {
		return nil, sqlErr
	}
	info := make(map[string]interface{})
	QueryRawSql(sqlData.Sql, sqlData.Args, &info)
	return info, nil
}
func GetDrySql(query models.CrudQuery) (models.SqlExplain, error) {
	if query.Sql != "" {
		return models.SqlExplain{
			Sql:  query.Sql,
			Args: query.GetArgValMap(),
		}, nil
	}
	sb := &bytes.Buffer{}
	sb.WriteString("SELECT ")
	sb.WriteString(query.Select)
	sb.WriteString(" FROM ")
	sb.WriteString(query.From)
	preSql := sb.String()
	if len(query.Where) >= 1 {
		whereMap, sqlErr := query.WhereSqlMap(preSql)
		if sqlErr != nil {
			return models.SqlExplain{}, sqlErr
		}
		for k, v := range whereMap {
			whereName := "${" + k + "}"
			if strings.Contains(preSql, whereName) {
				preSql = strings.ReplaceAll(preSql, whereName, v)
			} else {
				preSql = preSql + v
			}
		}
	}
	preSql = preSql + " limit 1"
	return models.SqlExplain{
		Sql:  preSql,
		Args: query.GetArgValMap(),
	}, nil
}
func DelDrySql(del models.CrudDel) (models.SqlExplain, error) {
	if del.Sql != "" {
		return models.SqlExplain{
			Sql:  del.Sql,
			Args: del.GetArgValMap(),
		}, nil
	}
	if del.Fake {
		return fakeDelDrySql(del.CrudQuery)
	}
	sb := &bytes.Buffer{}
	sb.WriteString("DELETE FROM ")
	sb.WriteString(del.From)
	preSql := sb.String()
	if len(del.Where) >= 1 {
		whereMap, sqlErr := del.WhereSqlMap(preSql)
		if sqlErr != nil {
			return models.SqlExplain{}, sqlErr
		}
		for k, v := range whereMap {
			whereName := "${" + k + "}"
			if strings.Contains(preSql, whereName) {
				preSql = strings.ReplaceAll(preSql, whereName, v)
			} else {
				preSql = preSql + v
			}
		}
	}
	return models.SqlExplain{
		Sql:  preSql,
		Args: del.GetArgValMap(),
	}, nil
}
func DelSql(del models.CrudDel) (int64, error) {
	sqlData, sqlErr := DelDrySql(del)
	if sqlErr != nil {
		return 0, sqlErr
	}
	return ExecRawSql(sqlData.Sql, sqlData.Args)
}
func fakeDelDrySql(del models.CrudQuery) (models.SqlExplain, error) {
	sb := &bytes.Buffer{}
	sb.WriteString("UPDATE ")
	sb.WriteString(del.Select)
	sb.WriteString(" SET del_flag=1 ")
	preSql := sb.String()
	if len(del.Where) >= 1 {
		whereMap, sqlErr := del.WhereSqlMap(preSql)
		if sqlErr != nil {
			return models.SqlExplain{}, sqlErr
		}
		for k, v := range whereMap {
			whereName := "${" + k + "}"
			if strings.Contains(preSql, whereName) {
				preSql = strings.ReplaceAll(preSql, whereName, v)
			} else {
				preSql = preSql + v
			}
		}
	}
	return models.SqlExplain{
		Sql:  preSql,
		Args: del.GetArgValMap(),
	}, nil
}
func UpdateSql(update models.CrudUpdate, updateItem map[string]interface{}) (int64, error) {
	sqlData, sqlErr := UpdateDrySql(update, updateItem)
	if sqlErr != nil {
		return 0, sqlErr
	}
	return ExecRawSql(sqlData.Sql, sqlData.Args)
}
func UpdateDrySql(update models.CrudUpdate, updateItem map[string]interface{}) (models.SqlExplain, error) {
	argMap := update.GetArgValMap()
	if update.Sql != "" {
		for k, v := range updateItem {
			argMap[k] = v
		}
		return models.SqlExplain{
			Sql:  update.Sql,
			Args: argMap,
		}, nil
	}
	sb := &bytes.Buffer{}
	sb.WriteString("UPDATE ")
	sb.WriteString(update.From)
	sb.WriteString(" SET ")
	assignments := make([]string, 0)
	for k, v := range updateItem {
		assignments = append(assignments, k+" = ${"+k+"}")
		argMap[k] = v
	}
	sb.WriteString(strings.Join(assignments, ","))
	preSql := sb.String()
	if len(update.Where) >= 1 {
		whereMap, sqlErr := update.WhereSqlMap(preSql)
		if sqlErr != nil {
			return models.SqlExplain{}, sqlErr
		}
		for k, v := range whereMap {
			whereName := "${" + k + "}"
			if strings.Contains(preSql, whereName) {
				preSql = strings.ReplaceAll(preSql, whereName, v)
			} else {
				preSql = preSql + v
			}
		}
	}
	return models.SqlExplain{
		Sql:  preSql,
		Args: argMap,
	}, nil
}
func InsertSql(add models.CrudAdd, argMap map[string]interface{}) (int64, error) {
	sqlData := InsertDrySql(add, argMap)
	return ExecRawSql(sqlData.Sql, sqlData.Args)
}
func InsertDrySql(add models.CrudAdd, argMap map[string]interface{}) models.SqlExplain {
	if add.Sql != "" {
		return models.SqlExplain{
			Sql:  add.Sql,
			Args: argMap,
		}
	}
	sb := &bytes.Buffer{}
	sb.WriteString("INSERT INTO ")
	sb.WriteString(add.Into)
	assignments := make([]string, 0)
	values := make([]string, 0)
	for k, _ := range argMap {
		assignments = append(assignments, k)
		values = append(values, "${"+k+"}")
	}
	sb.WriteString(" (")
	sb.WriteString(strings.Join(assignments, ", "))
	sb.WriteString(")")
	sb.WriteString(" VALUES ( ")
	sb.WriteString(strings.Join(values, ", "))
	sb.WriteString(")")
	return models.SqlExplain{
		Sql:  sb.String(),
		Args: argMap,
	}
}
